var CardContainer = require("./cardcontainer");
var BattleData = require("./battleData");
var BattleRecord = require('./battlerecord');
var Log = require('../framework/log');
var Card = require('./card').Card;
var CARD_TYPE = require('./card').CARD_TYPE;
var Messages = require('./messages');
var ACTION_TYPE = require('../../commons/common').ACTION_TYPE;
var Action = require('../../commons/common').Action;

var MAX_BATTLE_PLAYER = 2;

function Battle(battleId){
    this.battleDatas = {};
    this.battlePlayers = {};
    this.battlePlayerIds = [];
    this.player_num = 0;
    this.player_requested_num = 0;
    this.playerCards = {};
    this.battleRecord = new BattleRecord();
    this.battleId = battleId;
    this.battleChannel = null;
}


Battle.prototype.start = function(){
    this.battlePlayerIds = [];
    var channelName = "battle";
    for(var playerId in this.battlePlayers){
        if(this.battlePlayers.hasOwnProperty(playerId)){
            var player = this.battlePlayers[playerId];
            channelName = channelName + "_" + playerId;
            this.battlePlayerIds.push(playerId);
            this.playerCards[playerId] = new CardContainer();
            //this.playerCards[playerId].drawCard(player.getCardNum(),player.getOwnCards());
            this.battleDatas[playerId] = new BattleData();
            this.battlePlayers[playerId].copyBattleData(this.battleDatas[playerId]);
        }
    }
    //create channel
    this.battleChannel = app.getChannelService().newChannel(channelName);
    for(var playerId in this.battlePlayers){
        if(this.battlePlayers.hasOwnProperty(playerId)){
            var player = this.battlePlayers[playerId];
            this.battleChannel.enter(player);
        }
    }
    //draw card
    this.drawCard();
    Log.debug("[BATTLE]battle started,battle id = %s",this.battleId);
};

Battle.prototype.drawCard = function(){
    for(var playerId in this.battlePlayers){
        var player = this.battlePlayers[playerId];
        var cardContainer = this.playerCards[playerId];
        var cards = [];
        if(this.playerCards[playerId].drawCard(player.getMaxCardNum() - cardContainer.getCardNum(),player.getOwnCards(),cards)){
            this.battleRecord.pushDraw(playerId,this.battleRecord.round,cards);
        }
    }
};

Battle.prototype.getCards = function(playerId){
    return this.playerCards[playerId].getCards();
};

Battle.prototype.addBattlePlayer = function(player){
    this.battlePlayers[player.getPlayerId()] = player;
    ++this.player_num;
    Log.debug("[BATTLE]addBattlePlayer,battle id = %s,player = %s",this.battleId,player.getPlayerId());
};

Battle.prototype.delBattlePlayer = function(player){
    if(this.battlePlayers.hasOwnProperty(player.getPlayerId())){
        delete this.battlePlayers[player.getPlayerId()];
        --this.player_num;
        this.battleChannel.leave(player);
    }
    Log.debug("[BATTLE]delBattlePlayer,battle id = %s,player = %s",this.battleId,player.getPlayerId());
}

Battle.prototype.addViewPlayer = function(player){
    Log.debug("[BATTLE]addViewPlayer,battle id = %s,player = %s",this.battleId,player.getPlayerId());
};

Battle.prototype.removeViewPlayer = function(player){
    Log.debug("[BATTLE]removeViewPlayer,battle id = %s,player = %s",this.battleId,player.getPlayerId());
}

/*@param {Number} playerId
 * @param {Number} current round
 * @param {Array} index list of cards, eg [1,2,3,4] means cards with index 1,2,3,4 in CardContainer
 * @param {Bool} if guard mode
 * */
Battle.prototype.requestPlay = function(playerId,round,cards_index,isguard){
    this.battleRecord.pushRequest(playerId,round,cards_index,isguard);
    ++this.player_requested_num;
    //if all player requested play
    if(this.player_requested_num >= MAX_BATTLE_PLAYER){
        this.player_requested_num = 0;
        return this.playRound(round);
    }
    return false;
};
/*
 * 计算一轮的战斗结果，生成战斗序列，并且调整对应Player的数值：
 1、Attack：
 a）如果双方均防御,直接进入AfterAttack
 b）如果至少有一方非防御，需计算卡牌效果，按卡牌优先级决定顺序。SpecialAttack，Attack，Guard只有胜者有卡牌效果
 2、AfterAttack：计算双方Buffer的效果，Buffer按优先级轮流产生效果
 战斗过程：
 1、如果双方都是普通卡或者特殊攻击卡，进入拼点
 a）先判断是不是Combo（如果符合卡牌组合并且对方没有Combo无效Buffer）
 b）判断常驻Buffer是否有对判定点的加成
 c）再分别计算优先级，优先级大者胜
 d）如果拼点是平局，此轮结束
 c）如果拼点有胜方：
 i)计算胜方伤害：原始伤害=卡牌数值*武力+胜方Buffer加成
 ii)计算负方实际：伤害 = 有攻击无效特效？0:原始伤害 - 负方Buffer减成
 iii)计算伤害结果，如果Hp = 0，判断濒死特效
 d）计算负方出牌特效（如果有的话）
 e）计算伤害结束双方Buffer特效（加血、减血）
 2、如果有一方不是普通或者特殊攻击，跳过拼点，按攻击方是胜者开始流程
 3、如果双方均不是普通卡或者特殊攻击卡

 Action代表战斗某一方的动作，每一轮可能有多个动作对，每个动作对表示并行发送的两个动作（分别是战斗两方）

 */
Battle.prototype.playRound = function(round){
    if(round < this.battleRecord.round){
        return false;
    }
    //current must 2 player
    var hPlayerId = this.battlePlayerIds[0];
    var cPlayerId = this.battlePlayerIds[1];
    var hPlayer = this.battlePlayers[hPlayerId];
    var cPlayer = this.battlePlayers[cPlayerId];
    var hRequest = this.battleRecord.getRequest(hPlayerId,round);
    var cRequest = this.battleRecord.getRequest(cPlayerId,round);
    var hData = this.battleDatas[hPlayerId];
    var cData = this.battleDatas[cPlayerId];
    var hRequestCard = hRequest[0];
    var hIsGuard = hRequest[1];
    var cRequestCard = cRequest[0];
    var cIsGuard = cRequest[1];
    //replace card sequence into card with equal effect
    var cPlayCards = this.playerCards[cPlayerId].playCard(cRequestCard);
    var hPlayCards = this.playerCards[hPlayerId].playCard(hRequestCard);
    this.battleRecord.pushPlay(cPlayerId,this.battleRecord.round,cPlayCards);
    this.battleRecord.pushPlay(hPlayerId,this.battleRecord.round,hPlayCards);
    var cCard = Card.createEffectCard(cPlayCards);
    var hCard = Card.createEffectCard(hPlayCards);
    //if all player guard
    if(hIsGuard && cIsGuard){
        this.battleRecord.pushAction(round,hPlayerId,new Action(ACTION_TYPE.DEFENSE,null),cPlayerId,new Action(ACTION_TYPE.DEFENSE,null),null,null);
    }else if(hIsGuard){
        this.playEffect(cCard,cPlayerId,cData,hPlayerId,hData);
        action["targetAction"] = ACTION_TYPE.DEFENSE;
    }else if(cIsGuard){
        this.playEffect(hCard,hPlayerId,hData,cPlayerId,cData);
        action["targetAction"] = ACTION_TYPE.DEFENSE;
    }else{
        //if every one play attack, only winner play can take effect
        if((cCard.type === CARD_TYPE.ATTACK_CARD || cCard.type ===CARD_TYPE.SPECAIL_ATTACK_CARD)  && (hCard.type === CARD_TYPE.ATTACK_CARD || hCard.type === CARD_TYPE.SPECAIL_ATTACK_CARD)){
             var judgeResult = this.judge(hCard,hPlayerId,cCard,cPlayerId);
             if(judgeResult > 0){
                var action = this.playEffect(hCard,hPlayerId,cPlayerId);
             }else if( judgeResult < 0){
                 var action = this.playEffect(cCard,cPlayerId,hPlayerId);
             }else{
                 this.battleRecord.pushAction(round,cPlayerId,new Action(ACTION_TYPE.DRAW,null),hPlayerId,new Action(ACTION_TYPE.DRAW,null),cData,hData);
             }
        }else if(cCard.type === CARD_TYPE.ATTACK_CARD || cCard.type ===CARD_TYPE.SPECAIL_ATTACK_CARD ||  hCard.type === CARD_TYPE.ATTACK_CARD || hCard.type ===CARD_TYPE.SPECAIL_ATTACK_CARD){     //take effect in sequence
            var judgeResult = this.judge(hCard,hPlayerId,cCard,cPlayerId);
            if(judgeResult > 0){
                this.playEffect(hCard,hPlayerId,cPlayerId);
                this.playEffect(cCard,cPlayerId,hPlayerId);
            }else{
                this.playEffect(cCard,cPlayerId,hPlayerId);
                this.playEffect(hCard,hPlayerId,cPlayerId);
            }
        }else{   //take effect parallel
            this.playEffect(hCard,hPlayerId,cPlayerId);
            this.playEffect(cCard,cPlayerId,hPlayerId);
        }
    }
    if(this.battleRecord.round + 1 < BattleRecord.MAX_BATTLE_ROUND){
        this.battleRecord.newRound();
        this.drawCard();
    }
    this.notifyRoundResult();
    return true;
};

//judge who is winner
//@return {Number}  < 0 : cCard wins, == 0 : draw game, > 0: hCard wins
Battle.prototype.judge = function(hCard,hPlayerId,cCard,cPlayerId){
    var cBuffer = this.battleDatas[cPlayerId].buffer;
    var hBuffer = this.battleDatas[hPlayerId].buffer;
    var cNum = 0,hNum = 0;
    if(cBuffer &&cBuffer.type === CARD_TYPE.JUDGE_BUFFER_CARD && cCard.type === CARD_TYPE.ATTACK_CARD){
        cNum = cCard.params.num + cBuffer.params.num;
    }else{
        cNum = cCard.params.num;
    }
    if(hBuffer && hBuffer.type === CARD_TYPE.JUDGE_BUFFER_CARD && hCard.type === CARD_TYPE.ATTACK_CARD){
        hNum = hCard.params.num + hBuffer.params.num;
    }
    return cCard.params.num - hCard.params.num;
};

Battle.prototype.playEffect = function(card,selfPlayerId,targetPlayerId){
    var selfPlayer = this.battlePlayers[selfPlayerId];
    var targetPlayer = this.battlePlayers[targetPlayerId];
    var selfData = this.battleDatas[selfPlayerId];
    var targetData = this.battleDatas[targetPlayerId];
    if(card.name === "attack"){
        var dodge = this.checkHoldEffect(targetPlayerId,[CARD_TYPE.DODGE_CARD,CARD_TYPE.DODGE_COUNTER_CARD]);
        var damage = this.caclDamage(selfPlayer.getWuli(),1.0,selfPlayerId,targetPlayerId);
        if(dodge){
            this.battleRecord.pushAction(this.battleRecord.round,selfPlayerId,new Action(ACTION_TYPE.ATTACK,null),targetPlayerId,new Action(ACTION_TYPE.DODGE,null),selfData,targetData);
        }else{
            targetData.decHp(damage);
            this.battleRecord.pushAction(this.battleRecord.round,selfPlayerId,new Action(ACTION_TYPE.ATTACK,null),targetPlayerId,new Action(ACTION_TYPE.DAMAGE,damage),selfData,targetData);
        }
        //check if dead
        if(targetData.isDead()){
            var relive = this.checkHoldEffect(targetPlayerId,[CARD_TYPE.RELIVE_CARD]);
            if(relive){
                targetData.incHp(relive.params.hp);
                this.battleRecord.pushAction(this.battleRecord.round,targetPlayerId,new Action(ACTION_TYPE.RECOVER,relive.params.hp),selfPlayerId,null,selfData,targetData);
            }else{
                this.battleRecord.pushAction(this.battleRecord.round,targetPlayerId,new Action(ACTION_TYPE.DEAD,null),selfPlayerId,null,selfData,targetData);
            }
        }else{

        }
        //check if recounter
        var counter = this.checkHoldEffect(targetPlayerId,[CARD_TYPE.DODGE_COUNTER_CARD,CARD_TYPE.COUNTER_CARD]);
        if(counter){
            selfData.decHp(damage);
            this.battleDatas.pushAction(this.battleRecord.round,targetPlayerId,new Action(ACTION_TYPE.COUNTER,null),selfPlayerId,new Action(ACTION_TYPE.DAMAGE,damage),targetData,selfData);
        }
    }
};

Battle.prototype.caclDamage = function(wuli,factor,selfId,targetId){
    var damage = Math.floor(Math.sqrt(wuli) * 10/4);
    var selfBattleData = this.battleDatas[selfId];
    var targetBattleData = this.battleDatas[targetId];
    damage *= factor;
    if(selfBattleData.buffer && selfBattleData.buffer.type === CARD_TYPE.ATTACK_BUFFER_CARD){
        damage += selfBattleData.buffer.params["attack"];
    }
    if(targetBattleData.buffer && targetBattleData.buffer.type === CARD_TYPE.DEFENSE_BUFFER_CARD){
        damage -= targetBattleData.buffer.params["defense"];
    }
    return damage;
};

Battle.prototype.checkHoldEffect = function(playerId,effects){
    var cardContainer = this.playerCards[playerId];
    var candicates = [];
    for(var i = 0; i < cardContainer.cards.length; ++i){
        var card = cardContainer.cards[i];
        if(!card){
            continue;
        }
        for(var effect_index = 0; effect_index < effects.length; ++effect_index){
            if(card.type === effects[effect_index]){
                candicates.push(card);
            }
        }
    }
    if(candicates.length > 0){
        candicates.sort(function(a,b){
            return a.params.num - b.params.num;
        });
        return candicates[candicates.length - 1];
    }
    return null;
};

Battle.prototype.notifyRoundResult = function(){
    var result = {};
    for(var i in this.battlePlayerIds){
        var playerId = this.battlePlayerIds[i];
        result[playerId] = {};
        result[playerId].requests = this.battleRecord.requests[this.battleRecord.round - 1][playerId];
        result[playerId].playCards = this.battleRecord.playCards[this.battleRecord.round - 1][playerId];
        result[playerId].draw = this.battleRecord.draws[this.battleRecord.round][playerId];
    }
    result.action = this.battleRecord.actions[this.battleRecord.round - 1];
    this.battleChannel.notifyAll(new Messages.BattleRoundResultNotify(result));
};

module.exports = Battle;